package com.michael.demos.base.java.sort;

import java.util.Arrays;

/**
 * 类功能描述:
 * <pre>
 *   快速排序
 *   1. 从数列中挑出一个元素，称为 “基准”（pivot）;
 *   2. 重新排序数列，所有元素比基准值小的摆放在基准前面，所有元素比基准值大的摆在基准的后面（相同的数可以到任一边）。
 *      在这个分区退出之后，该基准就处于数列的中间位置。这个称为分区（partition）操作；
 *   3. 递归地（recursive）把小于基准值元素的子数列和大于基准值元素的子数列排序；
 * </pre>
 *
 * @author Michael
 * @version 1.0
 * @date 2020/9/24 9:31
 */
public class QuickSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) {
        // 对 arr 进行拷贝，不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        return quickSort(arr, 0, arr.length - 1);
    }

    private int[] quickSort(int[] arr, int left, int right) {
        System.out.println(left + "->" + right);
        if (left < right) {
            int partitionIndex = partition(arr, left, right);
            System.out.println(partitionIndex);
            quickSort(arr, left, partitionIndex - 1);
            quickSort(arr, partitionIndex + 1, right);
        }
        return arr;
    }

    /**
     * 分区操作，将目标数组中对应区间的数据分成小数区与大数区，并返回间隔的数组下标值
     * 1. pivotIndex 基准数值的下标值(当前以最左值作为基准值) —— 当遍历完才做数据交换
     * 2. intervalIndex 分区间隔下标值
     */
    private int partition(int[] arr, int left, int right) {
        int pivotIndex = left;
        int intervalIndex = pivotIndex;
        // 从基准值右1开始遍历
        for (int i = pivotIndex + 1; i <= right; i++) {
            if (arr[i] < arr[pivotIndex]) {
                wiseSwap(arr, i, intervalIndex + 1);
                intervalIndex++;
            }
        }
        wiseSwap(arr, pivotIndex, intervalIndex);
        return intervalIndex;
    }

    /** 有需要才做交换 */
    private void wiseSwap(int[] arr, int i, int j) {
        if (i != j) {
            System.out.println("开始转换" + i + " -> " + j);
            System.out.print("before\t");
            SortClient.printArray(arr);
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;

            System.out.print("after\t");
            SortClient.printArray(arr);
            System.out.println();
        } else {
            System.out.println("不变 " + i);
        }
    }
}
